﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Input;


namespace PonGun
{

    public enum CityNos
    {
        One,
        Two,
        Three,
        Four
    }

    public class City : Base2DObject
    {
        public PlayerIndex Player { get; set; }
        public CityNos CityNo { get; set; }
        public bool Destroyed { get; set; }

        public float Population { get; set; }

        TimeSpan lastGoodieSpawn { get; set; }
        public TimeSpan rateOfGoodieSpawn { get; set; }

        public bool CanSpawnGoodie { get { return (rateOfGoodieSpawn - (DateTime.Now.TimeOfDay - lastGoodieSpawn)) < TimeSpan.Zero; } }

        public List<Pickup> Pickups = new List<Pickup>();
        List<Pickup> DeadPickups = new List<Pickup>();

        Block Shield;
        
        public City(Game1 game, PlayerIndex player, CityNos cityNo)
            : base(game)
        {
            int X = 0, Y = 0;

            Player = player;
            CityNo = cityNo;

            rateOfGoodieSpawn = new TimeSpan(0, 0, 0, Game1.rnd.Next(10,31), 0);
            //lastGoodieSpawn = DateTime.Now.TimeOfDay + rateOfGoodieSpawn;
            Shield = new Block(game) { Width = 16, Height = 64, RegisteredCollider = false, };

            Width = 64;
            Height = 64;

            Population = 1;

            switch (Player)
            {
                case PlayerIndex.One:
                    X = Game1.RenderArea.Width - 40;
                    Color = Game1.Player1Color;
                    Asset = "Textures/Citys/CityR";
                    Shield.Asset = "Textures/ShieldR";
                    Shield.Position = new Vector2(X - 44, Y);
                    break;
                case PlayerIndex.Two:
                    X = 40;
                    Color = Game1.Player2Color;
                    Asset = "Textures/Citys/CityL";
                    Shield.Asset = "Textures/ShieldL";
                    Shield.Position = new Vector2(X + 44, Y);
                    break;
            }

            Shield.Color = Color;

            switch (CityNo)
            {
                case CityNos.One:
                    Y = 40;
                    break;
                case CityNos.Two:
                    Y = (int)Game1.RenderAreaCenter.Y - 85;
                    break;
                case CityNos.Three:
                    Y = (int)Game1.RenderAreaCenter.Y + 85;
                    break;
                case CityNos.Four:
                    Y = (int)Game1.RenderArea.Height - 40;
                    break;
            }
            
            Shield.Position = new Vector2(Shield.Position.X, Y);

            Position = new Vector2(X, Y);
            RegisteredCollider = true;
        }

        public void RepairCity()
        {
            Game.Content.Load<SoundEffect>("Audio/SFX/healCity").Play();
            Population = 1;
            ReSetTexture2D();
        }

        public void ReserectCity()
        {
            Game.Content.Load<SoundEffect>("Audio/SFX/resCity").Play();
            RepairCity();
            Destroyed = false;
            booming = false;
            RegisteredCollider = true;
        }

        public override void Update(GameTime gameTime)
        {
            if (Destroyed)
                return;

            base.Update(gameTime);

            if (CanSpawnGoodie)
            {
                lastGoodieSpawn = DateTime.Now.TimeOfDay;
                if(Game1.rnd.NextDouble() > .5)
                    SpawnGoodie();
            }

            if (HasShield)
            {
                if (Shield.RegisteredCollider)
                    Shield.Update(gameTime);
                else
                    HasShield = false;
            }

            foreach (Pickup pu in Pickups)
                pu.Update(gameTime);

            foreach (Pickup b in DeadPickups)
                Pickups.Remove(b);

            DeadPickups.Clear();
        }


        public void SpawnGoodie()
        {
            PickupType type = (PickupType)Enum.Parse(typeof(PickupType), Enum.GetName(typeof(PickupType), Game1.rnd.Next(0, (int)PickupType.LastRecord)));

            //switch (type) // Make it so there are less bad ones :S
            //{
            //    case PickupType.FlipH:
            //    case PickupType.FlipV:
            //    case PickupType.ScreenSpin:
            //        type = (PickupType)Enum.Parse(typeof(PickupType), Enum.GetName(typeof(PickupType), Game1.rnd.Next(0, (int)PickupType.LastRecord)));
            //        break;
            //}

            Pickups.Add(new Pickup(Game, this) { PickupTyp = type });
            Pickups[Pickups.Count - 1].OnCollided = PickupCollided;
            Pickups[Pickups.Count - 1].OnPickupDied = PickupDied;

            if (Player == PlayerIndex.One)
                Pickups[Pickups.Count - 1].Spawn(new Vector2(-1, 0));
            else
                Pickups[Pickups.Count - 1].Spawn(new Vector2(1, 0));
        }

        public void PickupDied(Base2DObject obj)
        {
            DeadPickups.Add((Pickup)obj);
        }

        void PickupCollided(CollisionData collisionData)
        {
            Base2DObject collidedWith = null;
            Pickup pickUp = null;
            if (collisionData.Objects[0] is Pickup)
            {
                pickUp = (Pickup)collisionData.Objects[0];
                collidedWith = (Base2DObject)collisionData.Objects[1];
            }
            else
            {
                pickUp = (Pickup)collisionData.Objects[1];
                collidedWith = (Base2DObject)collisionData.Objects[0];
            }

            if (pickUp.PickedUp)
                return;

            // atribute bat with hit..
            if (collidedWith is Bullet || collidedWith is Bat)
            {
                pickUp.PickedUp = true;

                Bat bat = null; 
                BatGun batGun = null;

                if (collidedWith is Bullet)
                {
                    bat = ((BatGun)((Bullet)collidedWith).Parent).Bat;
                    batGun = (BatGun)((Bullet)collidedWith).Parent;
                }
                else
                {
                    bat = (Bat)collidedWith;
                    Game.Content.Load<SoundEffect>("Audio/SFX/batpupickup").Play();
                }

                int mx = 0;
                List<City> cityLst;
                int bcnt = 0;

                switch (pickUp.PickupTyp)
                {
                    case PickupType.Ammo:
                        if (batGun != null)
                            batGun.Ammo += 100;
                        else
                        {
                            for (int g = 0; g < bat.Guns.Count; g++)
                                bat.Guns[g].Ammo += 100 / bat.Guns.Count;
                        }
                        Game.ShowPopUpMessage(new PopMsg() { message = "+100 Ammo", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.CityRepair:
                        // Pick a city adn fix it..
                        cityLst = Game.Cities.Where(entity => entity.Player == bat.Player && !entity.Destroyed).ToList();
                        mx = cityLst.Count;
                        if (mx > 0)
                            cityLst[Game1.rnd.Next(0, mx)].RepairCity();

                        Game.ShowPopUpMessage(new PopMsg() { message = "City Repair", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.CityReserection:
                        // Pick a dead city
                        cityLst = Game.Cities.Where(entity => entity.Player == bat.Player && entity.Destroyed).ToList();
                        mx = cityLst.Count;
                        if (mx > 0)
                            cityLst[Game1.rnd.Next(0, mx)].ReserectCity();

                        Game.ShowPopUpMessage(new PopMsg() { message = "City Resurection", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.Gun:
                        // Add another gun on
                        if(Game.AddGun(bat))
                            Game.ShowPopUpMessage(new PopMsg() { message = "GUNS!!", Color = pickUp.Color, position = pickUp.Position });
                        else
                        {
                            Game.IncreaseRateOfFire(bat);
                            Game.ShowPopUpMessage(new PopMsg() { message = "FASTER GUNS!!", Color = pickUp.Color, position = pickUp.Position });
                        }
                        
                        break;
                    case PickupType.Heal:
                        // Fix bat
                        Game.Content.Load<SoundEffect>("Audio/SFX/healCity").Play();
                        bat.ReSetTexture2D();
                        Game.ShowPopUpMessage(new PopMsg() { message = "Bat Repair", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.Nova:
                        // Nova ball
                        // Get back to a single ball
                        Game.ClearExcessBalls();
                        if (!Game.RenderNova)
                            Game.RenderNova = true;
                        else
                            Game.RenderNova = false;

                        Game.ShowPopUpMessage(new PopMsg() { message = "NOVA!!", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.Shield:
                        // Put a shield on a city
                        cityLst = Game.Cities.Where(entity => entity.Player == bat.Player && !entity.Destroyed && !entity.HasShield).ToList();
                        if (cityLst.Count > 0)
                        {
                            mx = cityLst.Count;
                            cityLst[Game1.rnd.Next(0, mx)].HasShield = true;
                        }                        
                        Game.ShowPopUpMessage(new PopMsg() { message = "City Shield", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.MultiBall:
                        Game.MultiBall();
                        Game.ShowPopUpMessage(new PopMsg() { message = "Multi Ball", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.MinusBall:                        
                        Game.RemoveBall();
                        Game.ShowPopUpMessage(new PopMsg() { message = "Ball Loss", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.FlipH:
                        if (Game1.ScreenSpriteEffect != SpriteEffects.FlipHorizontally)
                            Game1.ScreenSpriteEffect = SpriteEffects.FlipHorizontally;
                        else
                            Game1.ScreenSpriteEffect = SpriteEffects.None;

                        Game.ShowPopUpMessage(new PopMsg() { message = "Flip Horizontally", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.FlipV:
                        if (Game1.ScreenSpriteEffect != SpriteEffects.FlipVertically)
                            Game1.ScreenSpriteEffect = SpriteEffects.FlipVertically;
                        else
                            Game1.ScreenSpriteEffect = SpriteEffects.None;
                        Game.ShowPopUpMessage(new PopMsg() { message = "Flip Vertically", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.ScreenSpin:
                        if (!Game1.ScreenRotating)
                            Game1.ScreenRotating = true;
                        Game.ShowPopUpMessage(new PopMsg() { message = "Spin Screen", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.Mist:
                        if (!Game.RenderMist)
                            Game.RenderMist = true;
                        Game.ShowPopUpMessage(new PopMsg() { message = "Mitsy mist...", Color = pickUp.Color, position = pickUp.Position });
                        break;
                    case PickupType.LevelUp:
                        Game.Content.Load<SoundEffect>("Audio/SFX/levelUp").Play();
                        Game.Level += 1;
                        Game.ShowPopUpMessage(new PopMsg() { message = "Level UP!", Color = pickUp.Color, position = pickUp.Position });
                        break;
                }
            }            
        }

        bool _HasShield { get; set; }
        public bool HasShield 
        {
            get
            {
                return _HasShield;
            }
            set
            {
                if (value && !_HasShield)
                {
                    Game.Content.Load<SoundEffect>("Audio/SFX/shieldUp").Play();
                    Shield.RegisteredCollider = true;
                }
                if (!value)
                {
                    collisionManager.UnRegister(Shield, false);
                    Shield.RegisteredCollider = false;
                }

                _HasShield = value;
            }
        }

        public override void Collided(CollisionData collisionData)
        {
            base.Collided(collisionData);

            Base2DObject collidedWith = null;
            if (collisionData.Objects[0] == this)
                collidedWith = (Base2DObject)collisionData.Objects[1];
            else
                collidedWith = (Base2DObject)collisionData.Objects[0];

            if (collidedWith is Ball || collidedWith is Bullet)
            {
                int sc = 0;
                if (collidedWith is Ball)
                {
                    sc = Game.Bats.SingleOrDefault(entity => entity.Player == Player).Score -= 50 * Game.Level;
                    
                    Population -= .05f;
                }

                if (collidedWith is Bullet)
                {
                    sc = Game.Bats.SingleOrDefault(entity => entity.Player == Player).Score -= 25 * Game.Level;
                    Population -= .025f;
                }

                if (sc < 0)
                    Game.Bats.SingleOrDefault(entity => entity.Player == Player).Score = 0;

                if (Population <= 0 && !booming)
                    DestroyCity();
            }
        }

        public void DestroyCity()
        {
            if(Player == PlayerIndex.One)
                Game.Content.Load<SoundEffect>("Audio/SFX/bomb-02").Play();
            else
                Game.Content.Load<SoundEffect>("Audio/SFX/bomb-03").Play();

            // boom
            if (!booming)
                Boom();
        }
        bool booming = false;
        public void boomDone(Base2DObject p)
        {
            if (!Destroyed)
            {
                Destroyed = true;
                RegisteredCollider = false;
                collisionManager.UnRegister(this, false);

                HasShield = false;
                Shield.ReSetTexture2D();

                foreach (Pickup pu in Pickups)
                    pu.KillPickup(new CollisionData(pu, pu));

                foreach (Pickup b in DeadPickups)
                    Pickups.Remove(b);

                Pickups.Clear();
                DeadPickups.Clear();
            }
        }

        public void Boom()
        {
            booming = true;
            ParticleStrikeEffect(Game1.leftNormal, Position, Color);
            ParticleStrikeEffect(Game1.rightNormal, Position, Color);
            ParticleStrikeEffect(Game1.topNormal, Position, Color);
            ParticleStrikeEffect(Game1.botNormal, Position, Color);

            Vector2 op = new Vector2(-halfWidth, -halfHeight);
            ParticleStrikeEffect(Game1.leftNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.rightNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.topNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.botNormal, Position + op, Color);

            op = new Vector2(-halfWidth, halfHeight);
            ParticleStrikeEffect(Game1.leftNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.rightNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.topNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.botNormal, Position + op, Color);

            op = new Vector2(halfWidth, halfHeight);
            ParticleStrikeEffect(Game1.leftNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.rightNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.topNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.botNormal, Position + op, Color);

            op = new Vector2(halfWidth, -halfHeight);
            ParticleStrikeEffect(Game1.leftNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.rightNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.topNormal, Position + op, Color);
            ParticleStrikeEffect(Game1.botNormal, Position + op, Color);
        }

        public void ParticleStrikeEffect(Vector2 normal,Vector2 position, Color color)
        {
            Vector2 orgNorm = normal;
            TimeSpan lifetime = new TimeSpan(0, 0, 0, 0, 500);
            float fadeSpeed = .1f;

            Vector2 velMul = Vector2.One * MathHelper.Lerp(4, 8, (float)Game1.rnd.NextDouble());
            Game.testEmitter.AddParticle(new Particle(Game) { Position = position, Velocity = normal * velMul, Spin = -.1f, LifeSpan = lifetime, FadeSpeed = fadeSpeed, Color = color, OnParticleDied = boomDone });

            if (orgNorm.X == 0)
                normal.X -= .25f;

            if (orgNorm.Y == 0)
                normal.Y -= .25f;

            velMul = Vector2.One * MathHelper.Lerp(4, 8, (float)Game1.rnd.NextDouble());
            Game.testEmitter.AddParticle(new Particle(Game) { Position = position, Velocity = normal * velMul, Spin = -.1f, LifeSpan = lifetime, FadeSpeed = fadeSpeed, Color = color, OnParticleDied = boomDone });

            if (orgNorm.X == 0)
                normal.X -= .25f;

            if (orgNorm.Y == 0)
                normal.Y -= .25f;

            velMul = Vector2.One * MathHelper.Lerp(4, 8, (float)Game1.rnd.NextDouble());
            Game.testEmitter.AddParticle(new Particle(Game) { Position = position, Velocity = normal * velMul, Spin = -.1f, LifeSpan = lifetime, FadeSpeed = fadeSpeed, Color = color, OnParticleDied = boomDone });

            if (orgNorm.X == 0)
                normal.X += 1.25f;

            if (orgNorm.Y == 0)
                normal.Y += 1.25f;

            velMul = Vector2.One * MathHelper.Lerp(4, 8, (float)Game1.rnd.NextDouble());
            Game.testEmitter.AddParticle(new Particle(Game) { Position = position, Velocity = normal * velMul, Spin = -.1f, LifeSpan = lifetime, FadeSpeed = fadeSpeed, Color = color, OnParticleDied = boomDone });

            if (orgNorm.X == 0)
                normal.X += .25f;

            if (orgNorm.Y == 0)
                normal.Y += .25f;

            velMul = Vector2.One * MathHelper.Lerp(4, 8, (float)Game1.rnd.NextDouble());
            Game.testEmitter.AddParticle(new Particle(Game) { Position = position, Velocity = normal * velMul, Spin = -.1f, LifeSpan = lifetime, FadeSpeed = fadeSpeed, Color = color, OnParticleDied = boomDone });
        }

        public override bool Erroded(Base2DObject obj, bool errodBlock = false)
        {
            bool eroded = false;

            #region
            // remove pixels from the texture...

            //int xCol = (int)Math.Abs((Position.X - halfWidth) - (obj.Position.X - obj.halfWidth));
            //int yRow = (int)Math.Abs((Position.Y - halfHeight) - (obj.Position.Y - obj.halfHeight));

            int xCol = (int)Math.Abs((Position.X - halfWidth) - (obj.Position.X - obj.halfWidth));
            int yRow = (int)Math.Abs((Position.Y - halfHeight) - (obj.Position.Y - obj.halfHeight));

            Vector2 dir = obj.Position - Position;

            #region X
            if (dir.X > 0)
                xCol = Width - (int)((Position.X + halfWidth) - (obj.Position.X - obj.halfWidth)) - 1;

            if (dir.X < 0)
                xCol = (int)MathHelper.Clamp((obj.Position.X - obj.halfWidth) - (Position.X - halfWidth), 0, Width);

            if (dir.X == 0)
                xCol = halfWidth - obj.halfWidth;

            #endregion

            #region Y

            if (dir.Y > 0)
                yRow = Height - (int)((Position.Y + halfHeight) - (obj.Position.Y - obj.halfHeight)) - 1;

            if (dir.Y < 0)
                yRow = (int)MathHelper.Clamp((obj.Position.Y - obj.halfHeight) - (Position.Y - halfHeight), 0, Height);

            if (dir.Y == 0)
                yRow = halfHeight - obj.halfHeight;

            if (xCol < 0)
                xCol = 0;
            if (yRow < 0)
                yRow = 0;

            #endregion

            Vector2 colNormal = new Vector2(obj.Velocity.X > 0 ? -1 : 1, 0);
            int tookOut = 0;

            if (colNormal == Game1.leftNormal || colNormal == Game1.rightNormal)
            {
                for (int y = 0; y < obj.Height * 2; y++)
                {
                    if (colNormal == Game1.rightNormal)
                    {
                        for (int x = xCol, xcol = 0; x < Texture2D.Width && xcol < obj.Width * 2; x++, xcol++)
                        {
                            if (TextureMap[x + yRow * Texture2D.Width].A != 0)
                            {
                                if (errodBlock)
                                {
                                    TextureMap[x + yRow * Texture2D.Width].A = 0;

                                    for (int w = 1; w < obj.Height; w++)
                                    {
                                        if (yRow - w > 0 && Game1.rnd.NextDouble() > .5)
                                            TextureMap[x + (yRow - w) * Texture2D.Width].A = 0;

                                        if (w + yRow < Texture2D.Height - 1 && Game1.rnd.NextDouble() > .5)
                                            TextureMap[x + (yRow + w) * Texture2D.Width].A = 0;
                                    }
                                }


                                tookOut++;

                                eroded = true;

                                if (tookOut > obj.Width)
                                    break;

                            }
                        }
                    }

                    if (colNormal == Game1.leftNormal)
                    {
                        for (int x = xCol, xcol = 0; x < Texture2D.Width && xcol < obj.Width; x++, xcol++)
                        {
                            if (TextureMap[x + yRow * Texture2D.Width].A != 0)
                            {
                                if (errodBlock)
                                {
                                    TextureMap[x + yRow * Texture2D.Width].A = 0;

                                    for (int w = 1; w < obj.Height * 2; w++)
                                    {
                                        if (yRow - w > 0 && Game1.rnd.NextDouble() > .5)
                                            TextureMap[x + (yRow - w) * Texture2D.Width].A = 0;

                                        if (w + yRow < Texture2D.Height - 1 && Game1.rnd.NextDouble() > .5)
                                            TextureMap[x + (w + yRow) * Texture2D.Width].A = 0;
                                    }

                                }

                                tookOut++;

                                eroded = true;

                                if (tookOut > obj.Width)
                                    break;

                            }
                        }
                    }

                    yRow++;
                    if (yRow >= Texture2D.Height)
                        yRow = Texture2D.Height - 1;
                }
            }


            if (eroded && errodBlock)
                Texture2D.SetData<Color>(TextureMap);

            #endregion

            return eroded;
        }

        Color[] circle;
        int cw;
        int ch;
        public bool CityBallDamage(Ball ball)
        {
            bool hit = false;
            Vector2 center = GetContactPosition(ball);// -Position;

            if (circle == null)
            {
                Texture2D tmp = Game.Content.Load<Texture2D>("Textures/circle");
                circle = new Color[tmp.Width * tmp.Height];
                ch = tmp.Height;
                cw = tmp.Width;
                tmp.GetData<Color>(circle);
            }
            
            for (int x = (int)center.X, cx = 0; x < Texture2D.Width && cx < cw; x++, cx++)
            {
                for (int y = (int)center.Y, cy = 0; y < Texture2D.Height && cy < ch; y++, cy++)
                {
                    if (x < Texture2D.Width && y < Texture2D.Height)
                    {
                        if (circle[cx + cy * 8].A != 0 && TextureMap[x + y * Texture2D.Width].A != 0)
                        {
                            TextureMap[x + y * Texture2D.Width].A = 0;
                            hit = true;
                        }
                    }
                }
            }


            if (hit)
                Texture2D.SetData<Color>(TextureMap);

            return hit;
        }

        public override void Draw(GameTime gameTime)
        {
            if (!booming)
            {
                base.Draw(gameTime);

                if (HasShield)
                    Shield.Draw(gameTime);

                foreach (Pickup pu in Pickups)
                    pu.Draw(gameTime);
            }
        }
    }
}
